//
//  PageContentViewController.swift
//  PageNavigator
//
//  Created by 张奇 on 2025/8/13.
//

import UIKit

/// 分页容器数据源
public protocol PageContainerViewControllerDataSource: AnyObject {
    
    
    /// 分页有多少个数量
    /// - Parameter content: 容器
    /// - Returns: 数量
    func numberOfItems(in content: PageContainerViewController) -> Int
    
    
    /// 容器在index位置展示的viewController
    /// - Parameters:
    ///   - content: 容器
    ///   - index: 位置
    /// - Returns: viewController
    func pageContainerViewController(_ content: PageContainerViewController, viewControllerForItemAt index: Int) -> UIViewController

}

/// 分页容器代理
public protocol PageContainerViewControllerDelegate: AnyObject {
    
    
    /// 滚动中回调
    /// - Parameters:
    ///   - content: 容器
    ///   - from: 开始位置
    ///   - to: 结束为止
    ///   - percent: 百分比
    func pageContainerViewController(_ content: PageContainerViewController, scrollingFrom from: Int, to: Int, percent: CGFloat)
    
    
    /// 完全滚动到指定位置 可选
    /// - Parameters:
    ///   - content: 容器
    ///   - didScrollTo: 滚动到位置
    func pageContainerViewController(_ content: PageContainerViewController, didScrollTo to: Int)
}

public extension PageContainerViewControllerDelegate {
    func pageContainerViewController(_ content: PageContainerViewController, didScrollTo to: Int) {}
}


public class PageContainerViewController: UIViewController {
    
    
    private var collectionView: UICollectionView!
    
    //
    private var currentPage: Int = INVAILD_PAGE_INDEX
    private var nextPage: Int = INVAILD_PAGE_INDEX
    private var currentContentOffset = CGPoint.zero


    private var pageSize: Int = Int.max

    private var cacheController: [Int: UIViewController] = [:]
    
    //是否无限循环
    public var infiniteLoop = false
    
    // 数据源
    public weak var dataSource: PageContainerViewControllerDataSource?

    // 代理
    public weak var delegate: PageContainerViewControllerDelegate?
    
    private static let reuseId = "PageContentCollectionViewCell"
    private static let minInfiniteLoopCount = 3

    public override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        initUI()
        initData()
        doBeforeDisplay()
    }
    
    private func initData() {
        // 默认是第0个位置
        self.currentPage = 0
        self.currentContentOffset = CGPoint.zero
       
    }
    
    private func doBeforeDisplay() {
        if infiniteLoop {
            self.collectionView.scrollToItem(at: IndexPath.init(row: self.currentPage, section:1), at: UICollectionViewScrollPosition.left, animated: false);
        }
    }
    
    private func initUI() {
        view.backgroundColor = UIColor.black
        let layout = UICollectionViewFlowLayout()
        layout.minimumLineSpacing = 0
        layout.minimumInteritemSpacing = 0
        layout.estimatedItemSize = UIScreen().bounds.size
        layout.scrollDirection = UICollectionViewScrollDirection.horizontal
        
        let collectionView = UICollectionView(frame: self.view.bounds, collectionViewLayout: layout)
        collectionView.register(PageContentCollectionViewCell.self, forCellWithReuseIdentifier: PageContainerViewController.reuseId)
        collectionView.isPagingEnabled = true
        self.view.addSubview(collectionView)
        collectionView.delegate = self
        collectionView.dataSource = self
        self.collectionView = collectionView
        collectionView.backgroundColor = UIColor.white
    }
    
    
    public override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        self.collectionView.frame = self.view.bounds
    }
    
    // 选中到指定的位置去
    public func select(index: Int) {
        guard index != currentPage else {
            return
        }
        let indexPath = IndexPath(item: index, section: infiniteLoop ? 1 : 0)
        self.nextPage = index
        self.collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.left, animated: true)
    }
    
    public override func didMove(toParentViewController parent: UIViewController?) {
        super.didMove(toParentViewController: parent)
        if (nil != parent) {
            for (_, vc) in cacheController {
                vc.didMove(toParentViewController: self)
            }
        } else {
            for (_, vc) in cacheController {
                vc.didMove(toParentViewController: nil)
            }
        }
    }

    public override func willMove(toParentViewController parent: UIViewController?) {
        super.willMove(toParentViewController: parent)
    }

}


extension PageContainerViewController: UICollectionViewDataSource {
    
    public func numberOfSections(in collectionView: UICollectionView) -> Int {
        return infiniteLoop ? PageContainerViewController.minInfiniteLoopCount : 1;
    }
    
    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        let count = self.dataSource?.numberOfItems(in: self) ?? 0
        self.pageSize = count
        return count
    }
    
    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let index = indexPath.item
        guard let vc = dataSource?.pageContainerViewController(self, viewControllerForItemAt: index) else {
            fatalError("PageContentViewController cannot return nil in the \(index)")
        }
        
        if let old = cacheController[index], old != vc {
            //需要将原来的移除
            old.removeFromParentViewController()
            old.didMove(toParentViewController: nil)
        }
        if self != vc.parent {
            //需要添加进来
            self.addChildViewController(vc)
            vc.didMove(toParentViewController: vc)
            cacheController[index] = vc
        }
        
        return collectionView.dequeueReusableCell(withReuseIdentifier: PageContainerViewController.reuseId,
                                                  for: indexPath)
    }
}


extension PageContainerViewController: UICollectionViewDelegateFlowLayout {
    public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let size = self.view.bounds.size
        return size
    }
    
    public func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        guard let cell = cell as? PageContentCollectionViewCell, let will = cacheController[indexPath.item] else {
            return
        }
        
        if currentPage != indexPath.item {
            cell.displayViewController(will)
        } else {
            will.beginAppearanceTransition(true, animated: false)
            cell.displayViewController(will)
            will.endAppearanceTransition()
        }
    }
    
    public func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        guard let cell = cell as? PageContentCollectionViewCell, let end = cacheController[indexPath.item] else {
            return
        }
        
        if currentPage != indexPath.item {
            cell.endDisplayViewController(end)
        } else {
            end.beginAppearanceTransition(false, animated: false)
            cell.endDisplayViewController(end)
            end.endAppearanceTransition()
        }
    }
    
    public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        let currentPage = calculatePage(contentOffset:scrollView.contentOffset, referenceWidth: scrollView.bounds.width)
        collectionView.scrollToItem(at: IndexPath(item: currentPage, section: infiniteLoop ? 1 : 0), at: UICollectionViewScrollPosition.left, animated: false)
        doProcessCollectionViewScrollTo(index: currentPage)
    }
    
    public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        // 动画停止
        let currentPage = calculatePage(contentOffset:scrollView.contentOffset, referenceWidth: scrollView.bounds.width)
        collectionView.scrollToItem(at: IndexPath(item: currentPage, section: infiniteLoop ? 1 : 0), at: UICollectionViewScrollPosition.left, animated: false)
        doProcessCollectionViewScrollTo(index: currentPage)
    }
    
    
    private func doProcessCollectionViewScrollTo(index: Int) {
        self.currentPage = index
        self.nextPage = index
        self.currentContentOffset = CGPoint.init(x: self.collectionView.bounds.width * CGFloat(index), y: 0)
        self.delegate?.pageContainerViewController(self, didScrollTo: index)
    }
    
    
    public func scrollViewDidScroll(_ scrollView: UIScrollView) {
        
        let isUserDragging = scrollView.isTracking || scrollView.isDragging || scrollView.isDecelerating
        let contentOffset = scrollView.contentOffset
        let bounds = scrollView.bounds
        let referenceWidth: CGFloat
        let nextPage: Int
        if isUserDragging {
            // 用户拖动滚动的
            nextPage = calculatePage(contentOffset: contentOffset, referenceWidth: bounds.width)
            if nextPage != self.nextPage && self.nextPage != self.currentPage {
                // 说明是连续变动
                doProcessCollectionViewScrollTo(index: self.nextPage)

            }
            referenceWidth = bounds.width
            self.nextPage = nextPage
        } else {
            // segment 选择滚动的
            nextPage = self.nextPage
            referenceWidth = bounds.width * (CGFloat)(abs(nextPage - currentPage))
        }
        
        
        let percent: CGFloat
        if  currentPage > nextPage {
            //向左滑
            let deltaX = CGFloat((currentPage)) * bounds.width - contentOffset.x
            percent = CGFloat((deltaX / referenceWidth))
        } else {
            //向右滑
            let deltaX = contentOffset.x - CGFloat((currentPage)) * bounds.width
            percent = CGFloat(deltaX / referenceWidth)
        }
        self.delegate?.pageContainerViewController(self, scrollingFrom: currentPage, to: nextPage, percent: percent)
    }
    
    func calculatePage(contentOffset: CGPoint, referenceWidth: CGFloat) -> Int {
        let p: Int
        if contentOffset.x > self.currentContentOffset.x {
            //
            p = Int(ceil(contentOffset.x / referenceWidth))
        } else {
            p = Int(contentOffset.x / referenceWidth)
        }
       
//        let willPage: Int
//        if infiniteLoop {
//            willPage = p % pageSize
//        } else {
//            willPage = min(max(0, p), pageSize)
//        }
        return min(max(0, p % pageSize), pageSize)
    }
}
